//----------------------------------Include-------------------------------------
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "mcu.h"
#include "glbvar.h"
#include "calc.h"
//------------------------------------------------------------------------------

//-----------------------------------Macro--------------------------------------
#define _SAMP_LEN       GM_SAMP_LEN
#define _ADC_VREF       (2.5)
//------------------------------------------------------------------------------

//---------------------------------Static Var-----------------------------------
//方便Cache类的API操作，做32字节对齐
#if defined ( __ICCARM__ )
#pragma location = 0x38000000
uint16_t ADCxValues[_SAMP_LEN *2];
#elif defined ( __CC_ARM )
ALIGN_32BYTES(__attribute__((section (".RAM_D3"))) uint16_t ADCxValues[_SAMP_LEN *2]);
#endif
//------------------------------------------------------------------------------
static ADC_HandleTypeDef   AdcHandle = {0};
static DMA_HandleTypeDef   Dma_Handle = {0};
//------------------------------------------------------------------------------

//--------------------------------Static Func-----------------------------------
//------------------------------------------------------------------------------

//--------------------------------Public Func-----------------------------------
void mcu_init_adc(void)
{
//ADC clock
  // 配置PLL2时钟为的72MHz，方便分频产生ADC最高时钟36MHz
  // 使用AHB时钟的话，无需配置，默认选择
  RCC_PeriphCLKInitTypeDef    PeriphClkInitStruct = {0};
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  PeriphClkInitStruct.PLL2.PLL2M = 25;
  PeriphClkInitStruct.PLL2.PLL2N = 504;
  PeriphClkInitStruct.PLL2.PLL2P = 7;
  PeriphClkInitStruct.PLL2.PLL2Q = 7;
  PeriphClkInitStruct.PLL2.PLL2R = 7;
  PeriphClkInitStruct.PLL2.PLL2RGE      = RCC_PLL2VCIRANGE_0;
  PeriphClkInitStruct.PLL2.PLL2VCOSEL   = RCC_PLL2VCOWIDE;
  PeriphClkInitStruct.PLL2.PLL2FRACN    = 0;
  PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;
  if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK){ while(1); }

//ADC
  __HAL_RCC_ADC3_CLK_ENABLE();
  
  AdcHandle.Instance                   = ADC3;
  AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_ASYNC_DIV2;      /* 采用PLL异步时钟，2分频，即 72MHz/2 = 36MHz */
  AdcHandle.Init.Resolution            = ADC_RESOLUTION_16B;        /* 16位分辨率 */
  AdcHandle.Init.ScanConvMode          = ADC_SCAN_DISABLE;          /* 扫描 */
  AdcHandle.Init.EOCSelection          = ADC_EOC_SINGLE_CONV;       /* EOC转换结束标志 */
  AdcHandle.Init.LowPowerAutoWait      = DISABLE;                   /* 禁止低功耗自动延迟特性 */
  AdcHandle.Init.ContinuousConvMode    = DISABLE;                   /* 禁止自动转换，采用tim触发 */
  AdcHandle.Init.NbrOfConversion       = 1;                         /* 使用了1个转换通道 */
  AdcHandle.Init.DiscontinuousConvMode = DISABLE;                   /* 禁止不连续模式 */
  AdcHandle.Init.NbrOfDiscConversion   = 1;                         /* 禁止不连续模式后，此参数忽略，此位是用来配置不连续子组中通道数 */
  AdcHandle.Init.ExternalTrigConv      = ADC_EXTERNALTRIG_T4_CC4;   /* 采用tim触发 */
  AdcHandle.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_RISING;    /* 采用软件触发的话，此位忽略 */
  AdcHandle.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR; /* DMA循环模式接收ADC转换的数据 */
//AdcHandle.Init.BoostMode                = DISABLE;                         /* ADC时钟低于20MHz的话，可以禁止boost */
  AdcHandle.Init.Overrun                  = ADC_OVR_DATA_OVERWRITTEN;        /* ADC转换溢出的话，覆盖ADC的数据寄存器 */
  AdcHandle.Init.LeftBitShift             = ADC_LEFTBITSHIFT_NONE;
  AdcHandle.Init.OversamplingMode         = DISABLE;                         /* 禁止过采样 */
  if(HAL_ADC_Init(&AdcHandle) != HAL_OK){ while(1); }
  
  ADC_ConfigureBoostMode(&AdcHandle);     //升压控制位boost自动设置
  if(HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK){ while(1); }    // 校准ADC，采用偏移校准
  
//pin
  //PC0--IN10+    PC1--IN10-
  __HAL_RCC_GPIOC_CLK_ENABLE();

  GPIO_InitTypeDef    GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin  = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  
//ADC channel
  //序列1，采样PC0引脚
  //采用PLL2时钟的话，ADCCLK = 72MHz / 8 = 9MHz
  //ADC采样速度，即转换时间 = 采样时间 + 逐次逼近时间
  //                        = 810.5 + 8.5(16bit)
  //                        = 820个ADC时钟周期
  //那么转换速度就是 9MHz / 820 = 10975Hz
  ADC_ChannelConfTypeDef    sConfig = {0};
  sConfig.Channel      = ADC_CHANNEL_10;              /* 配置使用的ADC通道 */
  sConfig.Rank         = ADC_REGULAR_RANK_1;          /* 采样序列里的第1个 */
  sConfig.SamplingTime = ADC_SAMPLETIME_64CYCLES_5;   /* 采样周期 */
  sConfig.SingleDiff   = ADC_SINGLE_ENDED;            /* 单端或差分 */
  sConfig.OffsetNumber = ADC_OFFSET_NONE;             /* 无偏移 */
  sConfig.Offset       = 0;                           /* 无偏移的情况下，此参数忽略 */
  sConfig.OffsetRightShift       = DISABLE;           /* 禁止右移 */
  sConfig.OffsetSignedSaturation = DISABLE;           /* 禁止有符号饱和 */
  if(HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK){ while(1); }
  
//DMA
  __HAL_RCC_DMA1_CLK_ENABLE();
  
  Dma_Handle.Instance                 = DMA1_Stream1;            /* 使用的DMA1 Stream1 */
  Dma_Handle.Init.Request             = DMA_REQUEST_ADC3;        /* 请求类型 */
  Dma_Handle.Init.Direction           = DMA_PERIPH_TO_MEMORY;    /* 传输方向 */
  Dma_Handle.Init.PeriphInc           = DMA_PINC_DISABLE;        /* 外设地址自增禁止 */
  Dma_Handle.Init.MemInc              = DMA_MINC_ENABLE;         /* 存储器地址自增使能 */
  Dma_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* 外设数据传输位宽选择半字，即16bit */
  Dma_Handle.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD; /* 存储器数据传输位宽选择半字，即16bit */
  Dma_Handle.Init.Mode                = DMA_CIRCULAR;            /* 单次/循环模式 */
  Dma_Handle.Init.Priority            = DMA_PRIORITY_HIGH;       /* 优先级 */
  Dma_Handle.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;    /* 禁止FIFO*/
  Dma_Handle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL; /* 禁止FIFO此位不起作用，用于设置阀值 */
  Dma_Handle.Init.MemBurst            = DMA_MBURST_SINGLE;       /* 禁止FIFO此位不起作用，用于存储器突发 */
  Dma_Handle.Init.PeriphBurst         = DMA_PBURST_SINGLE;       /* 禁止FIFO此位不起作用，用于外设突发 */
  if(HAL_DMA_Init(&Dma_Handle) != HAL_OK){ while(1); }
  
  __HAL_LINKDMA(&AdcHandle, DMA_Handle, Dma_Handle);    //关联ADC句柄和DMA句柄
  
  HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
  
//start
  //HAL_ADC_Start_DMA 会调用 HAL_DMA_Start_IT 开启 DMA 中断
  if(HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *)ADCxValues, _SAMP_LEN*2) != HAL_OK){ while(1); }
}
//------------------------------------------------------------------------------

//------------------------------------ISR---------------------------------------
void DMA1_Stream1_IRQHandler(void)
{
  if((DMA1->LISR & DMA_FLAG_TCIF1_5) != RESET){   /* 传输完成中断 */
    //1、使用此函数要特别注意，第1个参数地址要32字节对齐，第2个参数要是32字节的整数倍。
    //2、进入传输完成中断，当前DMA正在使用缓冲区的前半部分，用户可以操作后半部分。
    SCB_InvalidateDCache_by_Addr((uint32_t *)(&ADCxValues[_SAMP_LEN]), _SAMP_LEN *sizeof(uint16_t));  //缓存更新到内存，注意length是字节数，16位数据应该乘以2
    
    igu32_bufflag = 2;
    for(uint32_t i=0; i<_SAMP_LEN; i++){
      igaf_adc_u[i] = (float)(ADCxValues[_SAMP_LEN+i])*(_ADC_VREF/65536);
    }
    
    DMA1->LIFCR = DMA_FLAG_TCIF1_5;               /* 清除标志 */
  }

  if((DMA1->LISR & DMA_FLAG_HTIF1_5) != RESET){   /* 半传输完成中断 */
    //1、使用此函数要特别注意，第1个参数地址要32字节对齐，第2个参数要是32字节的整数倍。
    //2、进入半传输完成中断，当前DMA正在使用缓冲区的后半部分，用户可以操作前半部分。
    SCB_InvalidateDCache_by_Addr((uint32_t *)(&ADCxValues[0]), _SAMP_LEN *sizeof(uint16_t));  //缓存更新到内存，注意length是字节数，16位数据应该乘以2
    
    igu32_bufflag = 1;
    for(uint32_t i=0; i<_SAMP_LEN; i++){
      igaf_adc_u[i] = (float)(ADCxValues[i])*(_ADC_VREF/65536);
    }
    
    DMA1->LIFCR = DMA_FLAG_HTIF1_5;               /* 清除标志 */
  }
  
  if((DMA1->LISR & DMA_FLAG_TEIF1_5) != RESET){   /* 传输错误中断 */
    DMA1->LIFCR = DMA_FLAG_TEIF1_5;               /* 清除标志 */
  }
  if((DMA1->LISR & DMA_FLAG_DMEIF1_5) != RESET){  /* 直接模式错误中断 */
    DMA1->LIFCR = DMA_FLAG_DMEIF1_5;              /* 清除标志 */
  }
}
//------------------------------------EOF---------------------------------------

#if 0
  //序列2，采样Vbat/4
  sConfig.Channel      = ADC_CHANNEL_VBAT_DIV4;       /* 配置使用的ADC通道 */
  sConfig.Rank         = ADC_REGULAR_RANK_2;          /* 采样序列里的第1个 */
  sConfig.SamplingTime = ADC_SAMPLETIME_810CYCLES_5;  /* 采样周期 */
  sConfig.SingleDiff   = ADC_SINGLE_ENDED;            /* 单端输入 */
  sConfig.OffsetNumber = ADC_OFFSET_NONE;             /* 无偏移 */ 
  sConfig.Offset       = 0;                           /* 无偏移的情况下，此参数忽略 */
  sConfig.OffsetRightShift       = DISABLE;           /* 禁止右移 */
  sConfig.OffsetSignedSaturation = DISABLE;           /* 禁止有符号饱和 */
  if(HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK){ while(1); }
  
  //序列3，采样VrefInt
  sConfig.Channel      = ADC_CHANNEL_VREFINT;         /* 配置使用的ADC通道 */
  sConfig.Rank         = ADC_REGULAR_RANK_3;          /* 采样序列里的第1个 */
  sConfig.SamplingTime = ADC_SAMPLETIME_810CYCLES_5;  /* 采样周期 */
  sConfig.SingleDiff   = ADC_SINGLE_ENDED;            /* 单端输入 */
  sConfig.OffsetNumber = ADC_OFFSET_NONE;             /* 无偏移 */ 
  sConfig.Offset       = 0;                           /* 无偏移的情况下，此参数忽略 */
  sConfig.OffsetRightShift       = DISABLE;           /* 禁止右移 */
  sConfig.OffsetSignedSaturation = DISABLE;           /* 禁止有符号饱和 */
  if(HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK){ while(1); }

  //序列4，采样温度
  sConfig.Channel      = ADC_CHANNEL_TEMPSENSOR;      /* 配置使用的ADC通道 */
  sConfig.Rank         = ADC_REGULAR_RANK_4;          /* 采样序列里的第1个 */
  sConfig.SamplingTime = ADC_SAMPLETIME_810CYCLES_5;  /* 采样周期 */
  sConfig.SingleDiff   = ADC_SINGLE_ENDED;            /* 单端输入 */
  sConfig.OffsetNumber = ADC_OFFSET_NONE;             /* 无偏移 */ 
  sConfig.Offset       = 0;                           /* 无偏移的情况下，此参数忽略 */
  sConfig.OffsetRightShift       = DISABLE;           /* 禁止右移 */
  sConfig.OffsetSignedSaturation = DISABLE;           /* 禁止有符号饱和 */
  if(HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK){ while(1); }
#endif



